import { BezierEdge, BezierEdgeModel, h } from '@logicflow/core';
import { createApp, h as vh } from 'vue';
import { isActive, connect, disconnect } from './teleport';
import CustomLine from './CustomLine.vue';

function isMouseInElement(element, e) {
    const rect = element.getBoundingClientRect();
    return (
        e.clientX >= rect.left &&
        e.clientX <= rect.right &&
        e.clientY >= rect.top &&
        e.clientY <= rect.bottom
    );
}

const DEFAULT_WIDTH = 32;
const DEFAULT_HEIGHT = 32;

class CustomEdge2 extends BezierEdge {
    constructor() {
        super();
        this.isMounted = false;
        this.customLineApp = null;
        this.root = null;
        this.handleMouseUp = (e) => {
            this.props.graphModel.clearSelectElements();
            this.props.model.isSelected = true;
            const element = e.target.parentNode.parentNode.querySelector('.lf-custom-edge-wrapper');
            if (isMouseInElement(element, e)) {
                this.props.model.graphModel.deleteEdgeById(this.props.model.id);
            }
        };
    }

    /**
     * 渲染vue组件
     * @param root
     */
    renderVueComponent(root) {
        this.unmountVueComponent();
        this.root = root;
        const { graphModel } = this.props;
        if (root) {
            if (isActive()) {
                connect(
                    this.targetId(),
                    CustomLine,
                    root,
                    this.props.model,
                    graphModel,
                    (node, graph) => {
                        return { model: node, graph };
                    }
                );
            } else {
                this.customLineApp = createApp({
                    render: () => vh(CustomLine, { model: this.props.model })
                });
                this.customLineApp?.mount(root);
            }
        }
    }

    targetId() {
        return `${this.props.graphModel.flowId}:${this.props.model.id}`;
    }

    /**
     * 组件即将卸载勾子
     */
    componentWillUnmount() {
        if (super.componentWillUnmount) {
            super.componentWillUnmount();
        }
        if (isActive()) {
            console.log('unmount');
            disconnect(this.targetId());
        }
        this.unmountVueComponent();
    }

    /**
     * 卸载vue
     * @returns
     */
    unmountVueComponent() {
        if (this.customLineApp) {
            this.customLineApp.unmount();
            this.customLineApp = null;
        }
        if (this.root) {
            this.root.innerHTML = '';
        }
        return this.root;
    }

    getEdge() {
        const { model } = this.props;
        const id = model.id;
        const { customWidth = DEFAULT_WIDTH, customHeight = DEFAULT_HEIGHT } = model.getProperties();
        const { startPoint, endPoint, path, isAnimation, arrowConfig } = model;
        const animationStyle = model.getEdgeAnimationStyle();
        const {
            strokeDasharray,
            stroke,
            strokeDashoffset,
            animationName,
            animationDuration,
            animationIterationCount,
            animationTimingFunction,
            animationDirection
        } = animationStyle;
        const positionData = {
            x: (startPoint.x + endPoint.x - customWidth) / 2,
            y: (startPoint.y + endPoint.y - customHeight) / 2,
            width: customWidth,
            height: customHeight
        };
        const style = model.getEdgeStyle();
        const wrapperStyle = {
            width: customWidth,
            height: customHeight
        };

        setTimeout(() => {
            const s = document.getElementById(id);
            if (s && !this.isMounted) {
                this.isMounted = true;
                this.renderVueComponent(s);
            }
        }, 0);

        delete style.stroke;

        return h('g', {}, [
            h(
                'style',
                { type: 'text/css' },
                '.lf-edge{stroke:#afafaf}.lf-edge:hover{stroke: #3370FF;}'
            ),
            h('path', {
                d: path,
                ...style,
                ...arrowConfig,
                ...(isAnimation
                   ? {
                        strokeDasharray,
                        stroke,
                        style: {
                            strokeDashoffset,
                            animationName,
                            animationDuration,
                            animationIterationCount,
                            animationTimingFunction,
                            animationDirection
                        }
                    }
                    : {})
            }),
            h(
                'foreignObject',
                {
                    ...positionData,
                    y: positionData.y + 5,
                    x: positionData.x + 5,
                    style: {}
                },
                [
                    h('div', {
                        id,
                        style: { ...wrapperStyle },
                        className: 'lf-custom-edge-wrapper'
                    })
                ]
            )
        ]);
    }
}

class CustomEdgeModel2 extends BezierEdgeModel {
    getArrowStyle() {
        const arrowStyle = super.getArrowStyle();
        arrowStyle.offset = 1;
        arrowStyle.verticalLength = 0;
        return arrowStyle;
    }

    getEdgeStyle() {
        const style = super.getEdgeStyle();
        // svg属性
        style.strokeWidth = 2;
        style.stroke = '#BBBFC4';
        style.offset = 0;
        return style;
    }

    /**
     * 重写此方法，使保存数据是能带上锚点数据。
     */
    getData() {
        const data = super.getData();
        if (data) {
            data.sourceAnchorId = this.sourceAnchorId;
            data.targetAnchorId = this.targetAnchorId;
        }
        return data;
    }

    /**
     * 给边自定义方案，使其支持基于锚点的位置更新边的路径
     */
    updatePathByAnchor() {
        const sourceNodeModel = this.graphModel.getNodeModelById(this.sourceNodeId);
        const sourceAnchor = sourceNodeModel
           .getDefaultAnchor()
           .find((anchor) => anchor.id === this.sourceAnchorId);

        const targetNodeModel = this.graphModel.getNodeModelById(this.targetNodeId);
        const targetAnchor = targetNodeModel
           .getDefaultAnchor()
           .find((anchor) => anchor.id === this.targetAnchorId);
        if (sourceAnchor && targetAnchor) {
            const startPoint = {
                x: sourceAnchor.x,
                y: sourceAnchor.y
            };
            this.updateStartPoint(startPoint);
            const endPoint = {
                x: targetAnchor.x,
                y: targetAnchor.y
            };

            this.updateEndPoint(endPoint);
        }

        // 这里需要将原有的pointsList设置为空，才能触发bezier的自动计算control点。
        this.pointsList = [];
        this.initPoints();
    }

    setAttributes() {
        super.setAttributes();
        this.isHitable = true;
        this.zIndex = 0;
    }
}

export default {
    type: 'app-edge',
    view: CustomEdge2,
    model: CustomEdgeModel2
};